;;; Required packages: nasm, gcc
;;; compile commands:
;;;     nasm -f elf 07-square-eq.asm -o bin/07-square-eq.o
;;;     gcc -m32 -o bin/07-square-eq.out bin/07-square-eq.o

%define SYSCALL_EXIT 1                 ; system call sys_exit
%define FLOAT32_SIZE_BYTES 4           ; 4 bytes per float32

section .data 
    scan_fmt db "%f", 0x0

    x1_fmt db "x1 = %f", 0xa, 0x0
    x2_fmt db "x2 = %f", 0xa, 0x0
    x_fmt db "x = %f", 0xa, 0x0

    message_enter_abc db "Please enter coefficients a, b, c of equation ax^2 + bx + c = 0", 0xa, 0x0
    result_no_roots db "No roots.", 0xa, 0x0
    result_infinite_roots db "Infinite roots", 0xa, 0x0

    enter_a_fmt db "a = ", 0x0
    enter_b_fmt db "b = ", 0x0
    enter_c_fmt db "c = ", 0x0

section .bss
    a resb FLOAT32_SIZE_BYTES
    b resb FLOAT32_SIZE_BYTES
    c resb FLOAT32_SIZE_BYTES

    d resb FLOAT32_SIZE_BYTES
    x1 resb FLOAT32_SIZE_BYTES
    x2 resb FLOAT32_SIZE_BYTES

section .text 
    extern printf
    extern scanf
    global main 

main:   
    push message_enter_abc
    call printf
    add esp, 4

    ; printf (enter_a_fmt)
    push enter_a_fmt
    call printf
    add esp, 4

    ; scanf (scan_fmt, a)
    push a
    push scan_fmt 
    call scanf
    add esp, 8

    ; printf (enter_b_fmt)
    push enter_b_fmt
    call printf
    add esp, 4

    ; scanf (scan_fmt, &b)
    push b
    push scan_fmt 
    call scanf
    add esp, 8

    ; printf (enter_c_fmt)
    push enter_c_fmt
    call printf
    add esp, 4

    ; scanf (scan_fmt, &c)
    push c
    push scan_fmt 
    call scanf
    add esp, 8

    cmp DWORD [a], __float32__(0.0)
    je .aIsZero
    jmp .calcD

.aIsZero:
    cmp DWORD [b], __float32__(0.0)
    je .abAreZero

    push DWORD __float32__(-1.0)       ; push -1.0 in evaluation stack

                                       ; take value -1.0 from evaluation stack
    fld DWORD [esp]                    ; stack: [-1]
    add esp, 4                         ; shift evaluation stack into initial point
    fld DWORD [c]                      ; stack: [-1, c]
    fmul                               ; stack: [-c]

    fld DWORD [b]                      ; stack: [-c, b]
    fdiv                               ; stack: [-c / b]

    fstp DWORD [x1]                    ; stack: [], result written into x1
    
    sub esp, 8                         ; reserve stack for a float64 (%f requires float64 in printf)
    mov ebx, x1
    fld DWORD [ebx]                    ; load float32 in current stack place (32 bits)
    fstp QWORD [esp]                   ; store float64 (8087 does the conversion from float32 internally)
    push x_fmt 
    call printf
    add esp, 12

    jmp .done 

.abAreZero:
    cmp DWORD [c], __float32__(0.0)
    je .abcAreZero

    jmp .noRoots 

.abcAreZero:
    push result_infinite_roots
    call printf
    add esp, 4

    jmp .done

.calcD:
    fld DWORD [b]                      ; stack: [b]
    fld DWORD [b]                      ; stack: [b, b]
    fmul                               ; stack: [b * b]
    fld DWORD [a]                      ; stack: [b * b, a]
    fld DWORD [c]                      ; stack: [b * b, a, c]
    fmul                               ; stack: [b * b, a * c]
    push DWORD __float32__(4.0)        ; push 4.0 in evaluation stack
                                       ; take value 4.0 from evaluation stack
    fld DWORD [esp]                    ; stack: [b * b, a * c, 4]
    add esp, 4                         ; shift evaluation stack into initial point
    fmul                               ; stack: [b * b, 4 * a * c]
    fsub                               ; stack: [b * b - 4 * a * c]
    fstp DWORD [d]                     ; stack: [], result written into d 


    push DWORD __float32__(0.0)        ; push 0.0 in evaluation stack
                                       ; take value 0.0 from evaluation stack
    fld DWORD [esp]                    ; stack: [b * b, a * c, 4]
    add esp, 4                         ; shift evaluation stack into initial point
    fld DWORD [d]
    fcom
    mov eax, DWORD 0
    fstsw ax

    cmp eax, DWORD 0x3100              ; eax = 0x3100 -> less
    je .noRoots

    jmp .calcRoots

.calcRoots:
    ; calculate x1
    push DWORD __float32__(2.0)        ; push -1.0 in evaluation stack
                                       ; take value 2.0 from evaluation stack
    fld DWORD [esp]                    ; stack: [2]
    add esp, 4                         ; shift evaluation stack into initial point
    fld DWORD [a]                      ; stack: [2, a]
    fmul                               ; stack: [2 * a]
    push DWORD __float32__(-1.0)       ; push -1.0 in evaluation stack
                                       ; take value -1.0 from evaluation stack
    fld DWORD [esp]                    ; stack: [2 * a, -1]
    add esp, 4                         ; shift evaluation stack into initial point
    fld DWORD [b]                      ; stack: [2 * a, -1, b]
    fmul                               ; stack: [2 * a, -b]
    fld DWORD [d]                      ; stack: [2 * a, -b, d]
    fsqrt                              ; stack: [2 * a, -b, sqrt(d)]
    fadd                               ; stack: [2 * a, -b + sqrt(d)]
    fld
    fdiv                               ; stack: [(-b + sqrt(d)) / (2 * a)]
    fstp DWORD [x1]                    ; stack: [], result written into x1 

    ; calculate x2 
    push DWORD __float32__(2.0)        ; push -1.0 in evaluation stack
                                       ; take value 2.0 from evaluation stack
    fld DWORD [esp]                    ; stack: [2]
    add esp, 4                         ; shift evaluation stack into initial point
    fld DWORD [a]                      ; stack: [2, a]
    fmul                               ; stack: [2 * a]
    push DWORD __float32__(-1.0)       ; push -1.0 in evaluation stack
                                       ; take value -1.0 from evaluation stack
    fld DWORD [esp]                    ; stack: [2 * a, -1]
    add esp, 4                         ; shift evaluation stack into initial point
    fld DWORD [b]                      ; stack: [2 * a, -1, b]
    fmul                               ; stack: [2 * a, -b]
    fld DWORD [d]                      ; stack: [2 * a, -b, d]
    fsqrt                              ; stack: [2 * a, -b, sqrt(d)] 
    fsub                               ; stack: [2 * a, -b - sqrt(d)]
    fld
    fdiv                               ; stack: [(-b - sqrt(d)) / (2 * a)]
    fstp DWORD [x2]                    ; stack: [], result written into x2

    ; print result 
    sub esp, 8                         ; reserve stack for a float64 (%f requires float64 in printf)
    mov ebx, x1
    fld DWORD [ebx]                    ; load float32 in current stack place (32 bits)
    fstp QWORD [esp]                   ; store float64 (8087 does the conversion from float32 internally)
    push x1_fmt 
    call printf
    add esp, 12
    
    sub esp, 8                         ; reserve stack for a float64 (%f requires float64 in printf)
    mov ebx, x2
    fld DWORD [ebx]                    ; load float32 in current stack place (32 bits)
    fstp QWORD [esp]                   ; store float64 (8087 does the conversion from float32 internally)
    push x2_fmt 
    call printf
    add esp, 12

    jmp .done 

.noRoots:
    push result_no_roots
    call printf
    add esp, 4

    jmp .done

.done:
    mov eax, SYSCALL_EXIT
    mov ebx, 0                   
    int 0x80                      
